
#include "PhongMaterial.h"
#include "HitRecord.h"
#include "Light.h"
#include "Point.h"
#include "Primitive.h"
#include "Ray.h"
#include "RenderContext.h"
#include "Scene.h"
#include "Vector.h"
#include "Math.h"
using namespace std;

PhongMaterial::PhongMaterial(const Color& color, float Kd, float Ka, const Color& highlight, float exponent)
  :color(color), Kd(Kd), Ka(Ka), highlight(highlight), exponent(exponent)
{
}

PhongMaterial::~PhongMaterial()
{
}

void PhongMaterial::shade(Color& result, const RenderContext& context,
                               const Ray& ray, const HitRecord& hit, const Color& atten, int depth) const
{
	const Scene* scene = context.getScene();
	if (depth >= scene->getMaxRayDepth() || atten.maxComponent() <= scene->getMinAttenuation()) {
		result = Color(0, 0, 0);
		return;
	}
	
	const vector<Light*>& lights = scene->getLights();
	Point hitpos = ray.origin()+ray.direction()*hit.minT();
	Vector normal;
	hit.getPrimitive()->normal(normal, context, hitpos, ray, hit);
	double costheta = Dot(normal, ray.direction());
	  if(costheta > 0)
	    normal = -normal;
	const Object* world = scene->getObject();

	Color light = scene->getAmbient()*Ka;    
	Color phong_effect(0, 0, 0);
	Light*const* begin = &lights[0];
	Light*const* end = &lights[0]+lights.size();
	while (begin != end) {
		Color light_color;
		Vector light_direction;
		double dist = (*begin++)->getLight(light_color, light_direction, context, hitpos);
		double cosphi = Dot(normal, light_direction);
		if (cosphi > 0) {
			// Cast shadow rays...
			HitRecord shadowhit(dist);
			Ray shadowray(hitpos, light_direction);
			world->intersect(shadowhit, context, shadowray);
			if (!shadowhit.getPrimitive())
				// No shadows...
				light += light_color*(Kd*cosphi);
			Vector H = light_direction - ray.direction();
			H.normalize();
			double cos_alpha = Dot(H, normal);
			if(cos_alpha > 0) {
				double phong_term = pow(cos_alpha, this->exponent);
				phong_effect = phong_effect + light_color*this->highlight*phong_term;
			}
		}
	}
	result = light*this->color + phong_effect*highlight;
}
